﻿using Hx.ADSyncPlatform.ActiveDirectory;
using Hx.ADSyncPlatform.ActiveDirectory.Model;
using Hx.ADSyncPlatform.Common.Excel;
using Hx.ADSyncPlatform.Common.ExportExcel;
using Hx.ADSyncPlatform.Controllers.Base;
using Hx.ADSyncPlatform.Infrastructure;
using Hx.ADSyncPlatform.Infrastructure.Exceptions;
using Hx.ADSyncPlatform.Infrastructure.Page;
using Hx.ADSyncPlatform.Service;
using Hx.ADSyncPlatform.WebApi.Model;
using Hx.ADSyncPlatform.WebApi.Rpc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hx.ADSyncPlatform.Controllers
{
    /// <summary>
    /// 认证控制器
    /// </summary>
    [Route("api/ad/person")]
    [ApiController]
    [Authorize]
    public class PersonController : BaseController
    {
        private ActiveDirectoryClient activeDirectoryClient;

        private SystemConfig systemConfig;
        Microsoft.Extensions.Configuration.IConfiguration configuration;


        private Plat_DataPermissionService plat_DataPermissionService;

        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        private RpcConfig rpcConfig;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <param name="activeDirectoryClient"></param>
        public PersonController(IOptions<SystemConfig> myOptionsAccessor,
            IOptions<RpcConfig> rpcConfigAccessor,
            Plat_DataPermissionService plat_DataPermissionService, IServiceProvider serviceProvider, ActiveDirectoryClient activeDirectoryClient, Microsoft.Extensions.Configuration.IConfiguration configuration) : base(serviceProvider)
        {
            this.activeDirectoryClient = activeDirectoryClient;
            this.systemConfig = myOptionsAccessor.Value;
            this.configuration = configuration;
            this.rpcConfig = rpcConfigAccessor.Value;
            this.plat_DataPermissionService = plat_DataPermissionService;
            activeDirectoryClient.Persons.filterObjectByDataPermission = plat_DataPermissionService.FilterObjectHandler;
        }




        /// <summary>
        /// 人员列表
        /// </summary>
        /// <param name="pagination"></param>
        /// <param name="ou"></param>
        /// <param name="group"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        //[AllowAnonymous]
        //[HttpGet("list")]
        //public async Task<Result<PageResult<Person>>> List([FromQuery] Pagination pagination,
        //    string ou = "", string group = "", string name = "")
        //{
        //    PageResult<Person> pageResult = activeDirectoryClient.Persons.PageList(ou, name, pagination.pageNum, pagination.pageSize);
        //    //PageResult<Hx.ADSyncPlatform.ActiveDirectory.Model.User> pageResult =
        //    //    activeDirectoryClient.userApi.PageList(ou, group, name, pagination.pageNum, pagination.pageSize);

        //    return await base.Success<PageResult<Person>>(pageResult);

        //}

        /// <summary>
        /// 人员列表 分页
        /// </summary>
        /// <param name="cn"></param>
        /// <returns></returns>
        [HttpGet("pageList")]
        public async Task<Result<PageResult<Person>>> PageList([FromQuery] Pagination pagination, string searchScope = "current", string distinguishedName = null, string displayName = null)
        {
            distinguishedName = $"OU={ systemConfig.person.name}," + distinguishedName;
            List<string> systemOus = systemConfig.systemOU.Select(n => n.name).ToList();
            PageResult<Person> persons = activeDirectoryClient.Persons.PageList(distinguishedName, displayName, platManager.DataPermissions, systemOus, searchScope, pagination.pageNum, pagination.pageSize);

            return await base.Success<PageResult<Person>>(persons);

        }

        [HttpGet("searchPersonPage")]
        public async Task<Result<PageResult<Person>>> SearchPersonPage([FromQuery] Pagination pagination, string displayName = null)
        {
            PageResult<Person> persons = activeDirectoryClient.Persons.SearchPersonPage(displayName, pagination.pageNum, pagination.pageSize);
            return await base.Success<PageResult<Person>>(persons);
        }

        /// <summary>
        /// 人员信息
        /// </summary>
        /// <param name="samAccountName"></param>
        /// <returns></returns>
        [HttpGet("{samAccountName}/info")]
        public async Task<Result<Person>> Get([FromRoute] string samAccountName)
        {
            Person person = activeDirectoryClient.Persons.Single(samAccountName);

            return await base.Success<Person>(person);

        }


        /// <summary>
        /// 计算机信息
        /// </summary>
        /// <param name="samAccountName"></param>
        /// <returns></returns>
        [HttpGet("{samAccountName}/computers")]
        public async Task<Result<Dictionary<string, object>>> GetComputer([FromRoute] string samAccountName)
        {
            Dictionary<string, object> result = new Dictionary<string, object>();
            Person adPerson = activeDirectoryClient.Persons.Single(samAccountName);
            if (adPerson == null) return await Fail<Dictionary<string, object>>(msg: $"{samAccountName}已不存在");

            string workStations = adPerson.UserWorkstations;
            if (string.IsNullOrEmpty(workStations))
            {
                result.Add("loginType", "all");//账户登陆计算机方式  尽可以登陆指定计算机
                return await Success<Dictionary<string, object>>(result);
            }
            (List<Computer> privateComputers, List<Computer> publicComputers) = this.activeDirectoryClient.ResolveComputers(adPerson, systemConfig);

            result.Add("loginType", "appoint");
            result.Add("personal", privateComputers.Count);
            result.Add("public", publicComputers.Count);
            return await Success<Dictionary<string, object>>(result);

        }

        /// <summary>
        /// 用户私人计算机列表
        /// </summary>
        /// <param name="samAccountName">用户samAccountName</param>
        /// <param name="name">计算机账号</param>
        /// <param name="pagination"></param>
        /// <returns></returns>
        [HttpGet("{samAccountName}/personalComputers")]
        public async Task<Result<PageResult<Computer>>> PersonalComputers([FromRoute] string samAccountName, [FromQuery] Pagination pagination, string searchVal = "", bool? binded = null)
        {
            Person adPerson = activeDirectoryClient.Persons.Single(samAccountName);
            List<Computer> computers = this.activeDirectoryClient.GetPrivateComputers(adPerson, systemConfig);
            int total = computers.Count;
            int startNum = Math.Max(pagination.pageNum - 1, 0) * pagination.pageSize;
            var pageData = computers.Skip(startNum).Take(pagination.pageSize).ToList();
            PageResult<Computer> pageResult = new PageResult<Computer>(pageData, pagination.pageNum, total);
            return await base.Success<PageResult<Computer>>(pageResult);
        }


        /// <summary>
        ///  用户公共计算机列表
        /// </summary>
        /// <param name="samAccountName">公共计算机分页列表</param>
        /// <param name="name"></param>
        /// <param name="pagination"></param>
        /// <returns></returns>
        [HttpGet("{samAccountName}/publicComputers")]
        public async Task<Result<PageResult<Computer>>> PublicComputers([FromRoute] string samAccountName, string name, [FromQuery] Pagination pagination, string searchVal = "", bool? binded = null)
        {
            Person adPerson = activeDirectoryClient.Persons.Single(samAccountName);
            List<Computer> computers = this.activeDirectoryClient.GetPublicComputers(adPerson, systemConfig);
            int total = computers.Count;
            int startNum = Math.Max(pagination.pageNum - 1, 0) * pagination.pageSize;
            var pageData = computers.Skip(startNum).Take(pagination.pageSize).ToList();
            PageResult<Computer> pageResult = new PageResult<Computer>(pageData, pagination.pageNum, total);
            return await base.Success<PageResult<Computer>>(pageResult);
            //string distinguishedName = $"OU={ systemConfig.publicComputer.name}," + adPerson.ParentOU;
            //adPerson.UserWorkstations = adPerson.UserWorkstations.Replace(".", "-");
            //List<string> computerNames = adPerson.UserWorkstations.Split(",").ToList();

            //PageResult<Computer> pageResult = activeDirectoryClient.Computers.PublicComputersPageList(distinguishedName, name, searchVal, adPerson.UserWorkstations, binded, pagination.pageNum, pagination.pageSize);
            //if (!string.IsNullOrWhiteSpace(adPerson.UserWorkstations))
            //{

            //    pageResult.data.ForEach(
            //        computer =>
            //        {
            //            computer.Binded = computerNames.ToList().Contains(computer.ComputerName);

            //        });
            //}

            //return await base.Success<PageResult<Computer>>(pageResult);
        }


        /// <summary>
        /// 绑定私人计算机
        /// </summary>
        /// <param name="samAccountName"></param>
        /// <param name="computerAccount"></param>
        /// <returns></returns>
        [HttpPost("{samAccountName}/bindPersonalComputer")]
        public async Task<Result<bool>> BindPersonalComputer([FromRoute] string samAccountName, string computerAccount)
        {
            Computer computer = activeDirectoryClient.Computers.Single(computerAccount);

            if (computer == null) throw new BizException($"计算机【{computerAccount}】已不存在，请刷新后重试");

            Person adPerson = activeDirectoryClient.Persons.Single(samAccountName);
            List<string> computers = new List<string>();
            if (!string.IsNullOrWhiteSpace(adPerson.UserWorkstations)) computers = adPerson.UserWorkstations.Split(",").ToList();

            string unbindComputerName = computerAccount.Replace("$", "").ToLower();
            computer.Info = samAccountName;
            this.InvokeRpcSyncComputer(samAccountName, computers);
            activeDirectoryClient.Computers.Update(computer);
            computers.Add(unbindComputerName);
            adPerson.UserWorkstations = string.Join(",", computers.Distinct());
            activeDirectoryClient.Persons.Update(adPerson);
            return await base.Success<bool>(true);
        }


        /// <summary>
        /// 绑定公共计算机
        /// </summary>
        /// <param name="samAccountName"></param>
        /// <param name="computerAccount"></param>
        /// <returns></returns>
        [HttpPost("{samAccountName}/bindPublicComputer")]
        public async Task<Result<bool>> BindPublicComputer([FromRoute] string samAccountName, string computerAccount)
        {
            Computer computer = activeDirectoryClient.Computers.Single(computerAccount);

            if (computer == null) throw new BizException($"计算机【{computerAccount}】已不存在，请刷新后重试");

            Person adPerson = activeDirectoryClient.Persons.Single(samAccountName);
            List<string> computers = new List<string>();
            if (!string.IsNullOrWhiteSpace(adPerson.UserWorkstations)) computers = adPerson.UserWorkstations.Split(",").ToList();

            string unbindComputerName = samAccountName.Replace("$", "").ToLower();
            computers.Add(unbindComputerName);
            computers = computers.Distinct().ToList();
            adPerson.UserWorkstations = string.Join(",", computers);
            this.InvokeRpcSyncComputer(samAccountName, computers);
            activeDirectoryClient.Persons.Update(adPerson);
            return await base.Success<bool>(true);
        }


        /// <summary>
        /// 解绑计算机
        /// </summary>
        /// <param name="samAccountName"></param>
        /// <param name="computerType"></param>
        /// <returns></returns>
        [HttpPost("{samAccountName}/unbindComputer")]
        public async Task<Result<bool>> UnBindComputer([FromRoute] string samAccountName, string computerAccount, string computerType = "personal")
        {
            if (string.IsNullOrWhiteSpace(computerAccount))
            {
                throw new BizException("计算机名错误！");
            }
            Computer computer = activeDirectoryClient.Computers.Single(computerAccount);
            if (computer == null) throw new BizException($"计算机【{computerAccount}】已不存在，请刷新后重试");
            Person adPerson = activeDirectoryClient.Persons.Single(samAccountName);

            string workStations = adPerson.UserWorkstations;
            List<string> computers = adPerson.UserWorkstations.Split(",").ToList();
            string account = adPerson.SamAccountName;
            account = account.Replace(".", "-");
            if (account.Length > 12)
            {
                account = account.Substring(0, 12);
            }
            List<string> privateComputers = computers.Where(n => n.StartsWith(account)).ToList();

            if (privateComputers.Count == 1)
            {
                throw new BizException("不可以解绑所有计算机，至少保留一个！");
            }
            string unbindComputerName = computer.ComputerName;
            computers.Remove(unbindComputerName);
            adPerson.UserWorkstations = string.Join(",", computers);
            if (adPerson.UserWorkstations == "")
            {
                adPerson.UserWorkstations = null;
            }

            this.InvokeRpcSyncComputer(adPerson.SamAccountName, computers);
            switch (computerType)
            {
                case "personal": //私有计算机 需要清空计算机的info属性
                    {
                        computer.Info = null;
                        activeDirectoryClient.Computers.Update(computer);
                        break;
                    }
                case "public":
                    {
                        break;
                    }
            }

            activeDirectoryClient.Persons.Update(adPerson);
            return await base.Success<bool>(true);


        }
        /// <summary>
        /// 导出Excel
        /// </summary>
        [HttpPost("exportExcel")]
        public async Task<object> ExportPersonExcel(string searchScope = "current", string distinguishedName = null, string displayName = null)
        {
            try
            {
                distinguishedName = $"OU={ systemConfig.person.name}," + distinguishedName;
                List<string> systemOus = systemConfig.systemOU.Select(n => n.name).ToList();
                //查询人员List
                List<Person> persons = activeDirectoryClient.Persons.List(distinguishedName, displayName, platManager.DataPermissions, systemOus, searchScope);
                if (!persons.Any()) return await base.Fail("导出失败！");
                //转导出所需实体List
                List<ExportPersonModel> PersonModelData = mapper.Map<List<ExportPersonModel>>(persons);
                TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                //生成文件名
                string excelName = $"Person{Convert.ToInt64(ts.TotalSeconds)}.xlsx";
                List<(string, List<object>)> tuples =
                new List<(string, List<object>)>();
                tuples.Add(("人员", PersonModelData.Select(d => (object)d).ToList()));
                var stream = ExcelHelper.ExportExcels(tuples);
                if (stream != null)
                {
                    return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", excelName);
                }

                return await base.Fail("导出失败！");
            }
            catch (Exception ex)
            {
                return await base.Fail(ex.Message);
            }
        }

        [AllowAnonymous]
        /// <summary>
        /// 模板下载
        /// </summary>
        /// <returns></returns>
        [HttpGet("downloadPersonTemplate")]
        public IActionResult DownloadPersonTemplate()
        {
            string folderPath = AppSettings.Configuration.GetValue<string>("TemplateFolder");
            var FilePath = $"{folderPath}用户绑定计算机模板.xlsx";
            var stream = System.IO.File.OpenRead(FilePath);
            return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Path.GetFileName(FilePath));

        }
        /// <summary>
        /// 上传
        /// </summary>
        /// <returns></returns>
        [HttpPost("importPersonTemplate")]
        public async Task<object> ImportPersonTemplate(IFormFile filev)
        {
            var Files = Request.Form.Files[0];
            var webRootPath = AppSettings.Configuration.GetSection("UploadPath").Get<string>();
            string fullPath = ExcelHelper.SaveExcel(Files, webRootPath);
            if (!System.IO.File.Exists(fullPath)) return await base.Fail("没有找到文件，插入操作失败！");
            List<ImportPersonStateModel> results = new List<ImportPersonStateModel>();
            List<ImportPersonComputerModel> sourceData = new List<ImportPersonComputerModel>();
            using (FileStream file = new FileStream(fullPath, FileMode.Open, FileAccess.Read))
            {
                //获取需要被导入的excel工作薄
                IWorkbook workBook = new XSSFWorkbook(file);
                //判断Excel文件中有几个sheet表数据
                if (workBook.NumberOfSheets == 0) return await base.Fail("Excel文件中没有sheet表数据，无法导入数据库，请仔细检查文件！");

                for (int v = 0; v < workBook.NumberOfSheets; v++)
                {
                    //声明行变量
                    IRow row = null;
                    //声明sheet表变量
                    ISheet sheet = null;
                    //拿到Sheet数据表以后，遍历获取数据
                    sheet = workBook.GetSheetAt(v);
                    for (int i = 0; i < sheet.LastRowNum + 1; i++)
                    {
                        //说明：
                        //i=0      拿到的是excel列字段名称
                        //i>=1   拿到的是excel的行数据
                        //首先拿到excel第一行列字段属性名，进行判断是否符合导入数据库的模板规范

                        if (i == 0)
                        {
                            row = sheet.GetRow(i);
                            if (row.Cells[0].StringCellValue != "用户账号" || row.Cells[1].StringCellValue != "计算机名（多个计算机请用逗号分开）")
                                return await base.Fail("模板不匹配！");
                        }
                        else
                        {
                            //获取excel表中非 列名称属性的数据集合
                            row = sheet.GetRow(i);

                            var rowData = new ImportPersonComputerModel() { PersonSamAccountName = row.Cells[0].StringCellValue };

                            if (string.IsNullOrEmpty(row.Cells[1].StringCellValue))
                            {
                                rowData.ComputerSamAccountNames = new List<string>();
                            }
                            else
                            {
                                rowData.ComputerSamAccountNames = row.Cells[1].StringCellValue.Replace("，", ",").Split(",").ToList();
                            }
                            sourceData.Add(rowData);

                        }

                    }

                }


            }
            results = this.commonImportPersonComputer(sourceData);
            return await base.Success<List<ImportPersonStateModel>>(results);



        }


        /// <summary>
        /// 导入计算机
        /// </summary>
        /// <param name="body"></param>
        /// <returns></returns>
        [HttpPost("importComputerFromClipboard")]
        public async Task<object> ImportComputerFromClipboard([FromBody] dynamic body)
        {
            try
            {
                string text = body.value.ToString();
                text = text.Replace("；", ";").Replace("，", ",").Replace("（", "(").Replace("）", ")");
                List<string> rows = text.Split("\n").Where(n => !string.IsNullOrWhiteSpace(n)).ToList();
                List<ImportPersonComputerModel> sourceData = new List<ImportPersonComputerModel>();
                foreach (string row in rows)
                {
                    ImportPersonStateModel persons = new ImportPersonStateModel();
                    string[] tempArray = row.Split("|");
                    string account = tempArray[0];
                    if (account.Contains("@"))
                    {
                        account = account.Split("@")[0];
                    }
                    var rowData = new ImportPersonComputerModel() { PersonSamAccountName = account };
                    if (tempArray.Length > 1)
                    {
                        string computerPart = tempArray[1];
                        string[] computers = computerPart.Split(",");
                        rowData.ComputerSamAccountNames = computers.ToList();
                    }
                    sourceData.Add(rowData);
                }
                List<ImportPersonStateModel> results = this.commonImportPersonComputer(sourceData);
                return await base.Success<List<ImportPersonStateModel>>(results);
            }
            catch (Exception ex)
            {
                logger.Error(ex);
                return await base.Fail<object>("文本校验失败，请检查文本是否符合要求！");
            }

        }


        private List<ImportPersonStateModel> commonImportPersonComputer(List<ImportPersonComputerModel> importPersonComputerModels)
        {
            List<ImportPersonStateModel> results = new List<ImportPersonStateModel>();

            foreach (ImportPersonComputerModel importPersonComputerModel in importPersonComputerModels)
            {
                if (string.IsNullOrWhiteSpace(importPersonComputerModel.PersonSamAccountName))
                {
                    results.Add(new ImportPersonStateModel()
                    {
                        importState = "失败",
                        errorMsg = "人员为空"
                    });
                    continue;
                }
                Person person = activeDirectoryClient.Persons.Single(importPersonComputerModel.PersonSamAccountName);
                if (person == null)
                {
                    results.Add(new ImportPersonStateModel()
                    {
                        importState = "失败",
                        errorMsg = "未找到人员",
                        SamAccountName = importPersonComputerModel.PersonSamAccountName
                    });
                    continue;
                }
                string computerNameStr = "";
                if (importPersonComputerModel.ComputerSamAccountNames == null || !importPersonComputerModel.ComputerSamAccountNames.Any())
                {
                    results.Add(new ImportPersonStateModel()
                    {
                        importState = "失败",
                        SamAccountName = importPersonComputerModel.PersonSamAccountName,
                        errorMsg = "未填写待绑定的计算机数据！",
                    });
                    continue;
                }
                computerNameStr = string.Join(",", importPersonComputerModel.ComputerSamAccountNames);
                StringBuilder computerErrorBuidler = new StringBuilder();
                foreach (string computerName in importPersonComputerModel.ComputerSamAccountNames)
                {
                    string computerSamAccountName = computerName.Replace(".", "-");
                    string otherPersonName = person.SamAccountName.Replace(".", "-");
                    if (otherPersonName.Length > 12)
                    {
                        otherPersonName = otherPersonName.Substring(0, 12);
                    }
                    //otherPersonName += "c";
                    if (computerName.Contains("$")) throw new Exception("计算机名称不允许有$");
                    //判断计算机是公共还是私人
                    bool isPersonal = false;

                    // 新规则  isPersonal=pureComputerName.StartsWith(otherPersonName)|| this.activeDirectoryClient.IsOldComputer(pureComputerName);
                    isPersonal = computerSamAccountName.StartsWith(otherPersonName);
                    logger.Debug($"【computerSamAccountName:{computerSamAccountName}】otherPersonName:{otherPersonName}");
                    Computer computer = activeDirectoryClient.Computers.Single(computerName);
                    try
                    {
                        if (computer == null)
                        {
                            computer = activeDirectoryClient.Computers.Single(computerSamAccountName.ToLower());
                            if (computer == null)
                            {
                                //创建计算机
                                if (isPersonal)//私人计算机自动绑定
                                {
                                    activeDirectoryClient.CreatePersonalComputer(computerName, person, systemConfig.personComputer);
                                }
                                else
                                {
                                    //公共计算机不存在也直接绑定  
                                    //string publicComputerOrgDN = $"OU={systemConfig.publicComputer.name},{ person.ParentOU}";
                                    //activeDirectoryClient.CreatePublicComputer(person.ParentOU, computerName, systemConfig.publicComputer);
                                }
                            }
                        }
                        person.UserWorkstations = person.UserWorkstations ?? "";
                        List<string> computerNames = person.UserWorkstations.Split(",").ToList();
                        if (!computerNames.Contains(computerName)) computerNames.Add(computerName);
                        if (computerNames.Count > 0) person.UserWorkstations = string.Join(",", computerNames);
                        else person.UserWorkstations = null;
                        this.InvokeRpcSyncComputer(person.SamAccountName, computerNames);
                        this.activeDirectoryClient.Persons.Update(person);

                    }
                    catch (Exception ex)
                    {
                        logger.Error(ex);
                        computerErrorBuidler.AppendLine($"绑定【{computerName}】时出错，错误信息为:{ex.Message.ToString()};");
                        break;
                    }

                }

                bool isSuccess = computerErrorBuidler.Length == 0;
                results.Add(new ImportPersonStateModel()
                {
                    importState = isSuccess ? "成功" : "失败",
                    errorMsg = isSuccess ? "" : computerErrorBuidler.ToString(),
                    SamAccountName = importPersonComputerModel.PersonSamAccountName,
                    UserWorkstations = computerNameStr
                });

            }

            return results;
        }

        /// <summary>
        /// 调用远程接口同步计算机
        /// </summary>
        /// <param name="account"></param>
        /// <param name="computers">计算机列表</param>
        /// <returns></returns>
        private void InvokeRpcSyncComputer(string account, List<string> computers)
        {
            RpcClient rpcClient = new RpcClient(this.rpcConfig);
            List<string> otherComputers = activeDirectoryClient.GetOtherComputers(account, computers);
            string otherComputersStr = "";
            if (otherComputers != null)
            {
                otherComputersStr = string.Join(",", otherComputers);
            }
            RpcResponse response = rpcClient.SyncComputer(account, otherComputersStr);
            if (response.Code != "200")
            {
                throw new Exception(response.Msg);
                //logHelper.ExceptionToDb(response.Msg);
                //return false;
            }
        }
    }
}


